/* myserver.cc: sample server program */

#include "server.h"
#include "connection.h"
#include "connectionclosedexception.h"
#include "messagehandler.h"
#include "discobase.h"
#include "protocol.h"
#include "article.h"

#include <iostream>
#include <string>
#include <cstdlib>
#include <sstream>

using namespace std;
using namespace client_server;

/*
 * Encode an int into four bytes and send it over the connection with the most significant byte first.
 */
void sendInt(int send, Connection* conn)
{
	conn->write((send>>24)&0xFF);
	conn->write((send>>16)&0xFF);
	conn->write((send>>8)&0xFF);
	conn->write((send>>0)&0xFF);
}

/*
 * Send the string s over the connection.
 */
void writeString(const string& s, Connection* conn) {
	conn->write(protocol::Protocol::PAR_STRING);
	sendInt(s.size(), conn);
	for (size_t i = 0; i < s.size(); ++i)
		conn->write(s[i]);
}

int main(int argc, char* argv[]){
    if (argc != 2) {
        cerr << "Usage: myserver port-number" << endl;
        exit(1);
    }
    
    Server server(atoi(argv[1]));
    if (! server.isReady()) {
        cerr << "Server initialization error" << endl;
        exit(1);
    }
    DiscoBase db = DiscoBase();
    while (true) {
        Connection* conn = server.waitForActivity();
        if (conn != 0) {
            try {
                MessageHandler mh(*conn);
		if( mh.getCommand() == protocol::Protocol::COM_LIST_NG ){
			vector<pair<int, string> > list = db.list_newsgroups();
			conn->write(protocol::Protocol::ANS_LIST_NG);
			conn->write(protocol::Protocol::PAR_NUM);
			sendInt(list.size(), conn);			

			for(vector<pair<int, string> >::iterator i = list.begin(); i != list.end(); ++i){			
				pair<int, string> p = *i;
				conn->write(protocol::Protocol::PAR_NUM);
				sendInt(p.first, conn);
				writeString(p.second, conn);
			}
			conn->write(protocol::Protocol::ANS_END);
		}
		else if(mh.getCommand() == protocol::Protocol::COM_CREATE_NG){
			conn->write(protocol::Protocol::ANS_CREATE_NG);
			try{
				char* parameters = mh.getParameters();
				string name = MessageHandler::readString(parameters);
				cout << name << endl;
				db.create_newsgroup(name);
				conn->write(protocol::Protocol::ANS_ACK);
			}catch(DatabaseException e){
				conn->write(protocol::Protocol::ANS_NAK);
				conn->write(protocol::Protocol::ERR_NG_ALREADY_EXISTS);
			}
			conn->write(protocol::Protocol::ANS_END);		
		}
		else if(mh.getCommand() == protocol::Protocol::COM_DELETE_NG){
			conn->write(protocol::Protocol::ANS_DELETE_NG);
			try{	
				char* parameters = mh.getParameters();
				int i = MessageHandler::readInt(parameters);
				db.delete_newsgroup(i); 
				conn->write(protocol::Protocol::ANS_ACK); 
			}catch(DatabaseException e){
				conn->write(protocol::Protocol::ANS_NAK);
				conn->write(protocol::Protocol::ERR_NG_ALREADY_EXISTS);
			}
			conn->write(protocol::Protocol::ANS_END);
		}
		else if(mh.getCommand() == protocol::Protocol::COM_LIST_ART){
			conn->write(protocol::Protocol::ANS_LIST_ART);
			try{
				char* parameters = mh.getParameters();
				int i = MessageHandler::readInt(parameters);
				vector<pair<int, string> > list = db.list_articles(i);
				conn->write(protocol::Protocol::ANS_ACK);
				conn->write(protocol::Protocol::PAR_NUM);
				sendInt(list.size(), conn);
				for(vector<pair<int, string> >::iterator i = list.begin(); i != list.end(); ++i){
					pair<int, string> p = *i;
					conn->write(protocol::Protocol::PAR_NUM);
					sendInt(p.first, conn);
					writeString(p.second, conn);
				}
				
			}catch(DatabaseException e){
				conn->write(protocol::Protocol::ANS_NAK);
				conn->write(protocol::Protocol::ERR_NG_DOES_NOT_EXIST);
			}
			conn->write(protocol::Protocol::ANS_END);
		}
		else if(mh.getCommand() == protocol::Protocol::COM_CREATE_ART){
			conn->write(protocol::Protocol::ANS_CREATE_ART);
			try{
				char* parameters = mh.getParameters();
				int group = MessageHandler::readInt(parameters);
				string title = MessageHandler::readString(parameters);
				string author = MessageHandler::readString(parameters);
				string content = MessageHandler::readString(parameters);
				db.create_article(group, title, author, content);
				conn->write(protocol::Protocol::ANS_ACK);
			}catch(DatabaseException e){
				conn->write(protocol::Protocol::ANS_NAK); 
				conn->write(protocol::Protocol::ERR_NG_DOES_NOT_EXIST);
			}
			conn->write(protocol::Protocol::ANS_END);
		}
		else if(mh.getCommand() == protocol::Protocol::COM_DELETE_ART){
			conn->write(protocol::Protocol::ANS_DELETE_ART);
			try{
				char* parameters = mh.getParameters();
				int group = MessageHandler::readInt(parameters);
				int article = MessageHandler::readInt(parameters);
				db.delete_article(group, article);
				conn->write(protocol::Protocol::ANS_ACK);
			}catch(DatabaseException e){
				conn->write(protocol::Protocol::ANS_NAK); 
				conn->write(e.id);
			}
			conn->write(protocol::Protocol::ANS_END);
		}
		else if(mh.getCommand() == protocol::Protocol::COM_GET_ART){
			conn->write(protocol::Protocol::ANS_GET_ART);
			try{	
				char* parameters = mh.getParameters();
				int group = MessageHandler::readInt(parameters);
				int article = MessageHandler::readInt(parameters);
				Article art = db.get_article(group, article);
				conn->write(protocol::Protocol::ANS_ACK);
				writeString(art.get_name(), conn);
				writeString(art.get_author(), conn);
				writeString(art.get_content(), conn);
			}catch(DatabaseException e){
				conn->write(protocol::Protocol::ANS_NAK);
				conn->write(e.id);
			}
			conn->write(protocol::Protocol::ANS_END);			
		}		
            }
            catch (ConnectionClosedException&) {
                server.deregisterConnection(conn);
                delete conn;
                cout << "Client closed connection" << endl;
            }
        }
        else {
            server.registerConnection(new Connection);
            cout << "New client connects" << endl;
        }
    }

}
